Sections

Abstract

In this document, I want to provide a write-up of how this model is similar to and different from the versions previously implemented. This model is meant to provide assembly of multiple sites at the same time, that may, or may not, be connected in some fashion. Along the way, I will mention some of the input and output format that I am expect as documentation. At the end, I will be presenting some preliminary results. I especially want to use this as a vehicle to think about how to analyse those results.

Functions

Results, No Spatial Structure

First, we load up the preliminary results. In this case, we are loading a system in which 34 basal species and 66 consumer species form the pool for 10 unconnected environments. The pool and interaction matrix were assembled with the default parameters from Law and Morton’s 1996 work.

library(dplyr)
Warning: package ‘dplyr’ was built under R version 4.1.1

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
Warning: package ‘ggplot2’ was built under R version 4.1.1
library(plotly)
Warning: package ‘plotly’ was built under R version 4.1.1

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(ggfortify) # used for biplots of PCAs
Warning: package ‘ggfortify’ was built under R version 4.1.1
# library(RMTRCode2)
load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-None.RData")
)

Events

In total, 9320 events were used in these environments, with the species and environment invasion both randomly assigned. The number of arrival and extinction events were controlled to both be half of this number. We chose this number due to the coupon collecting problem. In particular, we use the result that the probability of encountering each species is bounded: \[\text{Pr}(\text{Draws} < n \log_{e} n + c n) \rightarrow \exp(-\exp(-c)) \text{ as } n \rightarrow \infty\] where \(n\) is the number of species and \(c\) is a constant. For our purposes, we choose \(c = 5\) so that we have a probability of about \(99.3\%\) of seeing each species in each environment. In practice, we failed to observe 2 species-environment combinations. Notably, nearly every species had at least one successful invasion; 5 did not.

The initial abundance was set to be 4000 times the elimination threshold, in line with work on minimum viable populations (Traill et al. 2007). The elimination threshold is admittedly more arbitrary, since it sets an effective individual-area relationship. For this calculation, I set it to \(10^{-4}\), in line with our previous calculations. This is large enough to avoid numerical difficulties from precision, while being low enough to represent a decent sized region.

Since each population is assembling simultaneously, I chose to use exponential waiting times for the inter-species arrival and extinction times. Note that these rates are shared between species and environments, but arrival and extinction are fully independent of each other. Species and environment affected in each event was chosen uniformly at random. The question then is how to set the rate.

To set the rate in this case, I chose to set it to the largest eigenvalue magnitude of the per-environment interaction matrices. This magnitude corresponds to the strongest response we can see from the interaction matrix and, if the interaction matrix is a good approximation for the Jacobian around a stable fixed point (which is not guaranteed), indicates the characteristic time scale of the decay to equilibrium. Hence, (overall) arrival rates and (overall) extinction rates should happen on the same timescale as the (largest) dynamics in the system. Since there are 10 environments, we should then expect that 10 characteristic time scales, on average, should occur in between arrival events in the same environment.

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)

obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)

Every vertical line is a species introduction or extinction by neutral dynamics.

Alpha Diversity plots

Intro

Perhaps more intriguing might be some sense of the biodiversity that we have in each system. We break the abundance results up by environment, then calculate the number of non-zero abundance curves at each time point. We also calculate the Shannon entropy (reminder: higher entropy means more uncertainty which means a flatter distribution).

Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Richness = mean(Richness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

So richness hovers around a similar regime throughout the majority of the simulation. Note in this plot that we have emphasised one environmental curve and superimposed the mean in black. We do manage to reach heights of 11 species in one environment, but these heights are shortlived. Instead, we seem to observe a (time and environment averaged) value of 4.4595443. (If we consider the first 10,000 time units as burn-in, we instead see a value of 4.5878608.)

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 8043 row(s) containing missing values (geom_path).
Warning: Removed 2297 row(s) containing missing values (geom_path).

Entropy has a similar, but highly erratic, behaviour. If one tries to follow one of the entropy curves, then one sees that they have fairly substantial periods of almost smooth behaviour followed by suddenly very noisy behaviour, and noise seems to be the dominant mode if one tries to examine the low alpha environment in the background. There are some easy to make predictions. Extinctions reduce the entropy in the system, as you become more certain about what remains. Analogously, arrivals increase the entropy. We can probably better see the relationship beyond these principles by plotting entropy against richness and connecting observations by environment and time.

Entropy-Richness
# ggplot2::ggplot(
#   Diversity %>% dplyr::filter(Environment < 4), 
#   ggplot2::aes(
#     x = Richness,
#     y = Entropy,
#     group = Environment,
#     color = Time
#   )
# ) + ggplot2::geom_path(
# ) + ggplot2::guides(
#   alpha = "none"
# )

plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))

It seems quite hard to tell, but there does not appear to be any particular orientation (clockwise, counter-clockwise) or similar pattern here.

Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 10986 row(s) containing missing values (geom_path).
Warning: Removed 2831 row(s) containing missing values (geom_path).

Evenness helps highlight that this is a high variance process but with a relatively constrained mean.

Environment Diversity

We can, of course, flip the idea on its head. Instead of examining the diversity of species within environments, we can look at the diversity of environments occupied by species. Since very few species end up occupying environments, we just look at richness. Unfortunately, this is quite a memory exhaustive task.

EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

One immediately interesting trend here is that very few species are present across more than 5 environments at a given time. Indeed, only 12, 14, 22, 30 are ever present in more than 5 environments at once. We can also examine how long these periods occur for by species by tabulation. We block times so that entries are all of the same unit length, and round the average richness during the given time unit.

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7
    1   65961 14392   440     0     0     0     0     0
    2   34771 31380 11353  3289     0     0     0     0
    3   63201 13004  1767  2821     0     0     0     0
    4   46359 21083 11443  1619   289     0     0     0
    5   30641 31555 16546  1341   710     0     0     0
    6   72058  8735     0     0     0     0     0     0
    7   75519  5274     0     0     0     0     0     0
    8   64756 15510   527     0     0     0     0     0
    9   77914  2879     0     0     0     0     0     0
    10  17812 38680 20196  4105     0     0     0     0
    11  52660 24897  3236     0     0     0     0     0
    12   9668  2677  9779 26641 18598  9921  2296  1213
    13  64628 15714   451     0     0     0     0     0
    14  12091 21311 14559 16445 15150   931   306     0
    15  68562 10733  1498     0     0     0     0     0
    16  69685 10908   200     0     0     0     0     0
    17  30631 33715 15508   939     0     0     0     0
    18  69209 11326   258     0     0     0     0     0
    19  62453 17604   736     0     0     0     0     0
    20  74863  3687  1850   393     0     0     0     0
    21  47899 25705  4187  3002     0     0     0     0
    22   3179 13471 20153 21767 16741  5308   174     0
    23  65132 14944   717     0     0     0     0     0
    24  65788 12316  2689     0     0     0     0     0
    25  66081 11111  3601     0     0     0     0     0
    26  41646 19406 19205   536     0     0     0     0
    27  48803 20446 10283  1261     0     0     0     0
    28  68979 11814     0     0     0     0     0     0
    29  34625 26990 17051  2127     0     0     0     0
    30   5560 19062 20684 11363 11600  7960  3304  1260
    31  65010 15673   110     0     0     0     0     0
    32  19323 26016 29600  5854     0     0     0     0
    33  15126 23401 24348 10061  7857     0     0     0
    34  76427  4366     0     0     0     0     0     0
    35  73824  6084   885     0     0     0     0     0
    36  71044  9749     0     0     0     0     0     0
    37  38197 22367 15666  4486    77     0     0     0
    38  28748 35323 16722     0     0     0     0     0
    39  77005  3788     0     0     0     0     0     0
    40  49949 27132  3712     0     0     0     0     0
    41  79837   956     0     0     0     0     0     0
    42  78309  2484     0     0     0     0     0     0
    43  32813 23889 17055  6664   372     0     0     0
    44  39693 23982 15556  1562     0     0     0     0
    45  80793     0     0     0     0     0     0     0
    46  48455 27672  4666     0     0     0     0     0
    47  12049 36783 14830 15180  1951     0     0     0
    48  63147 16984   662     0     0     0     0     0
    49  73123  7670     0     0     0     0     0     0
    50  79858   935     0     0     0     0     0     0
    51  80254   539     0     0     0     0     0     0
    52  73942  6851     0     0     0     0     0     0
    53  77578  3215     0     0     0     0     0     0
    54  76396  4397     0     0     0     0     0     0
    55  55211 23955  1627     0     0     0     0     0
    56  72559  8234     0     0     0     0     0     0
    57  57438 22648   707     0     0     0     0     0
    58  78987  1806     0     0     0     0     0     0
    59  35310 37944  7539     0     0     0     0     0
    60  67875 12918     0     0     0     0     0     0
    61  52220 24754  3819     0     0     0     0     0
    62  62514 16665  1614     0     0     0     0     0
    63  46928 31448  2417     0     0     0     0     0
    64  68971 11215   607     0     0     0     0     0
    65  80793     0     0     0     0     0     0     0
    66  67126 12564  1103     0     0     0     0     0
    67  37877 27042 11836  4038     0     0     0     0
    68  51133 27052  2205   403     0     0     0     0
    69  61785 16497  2511     0     0     0     0     0
    70  73226  7359   208     0     0     0     0     0
    71  22115 22511 20022 14177  1968     0     0     0
    72  10056 26079 33451  7593  3614     0     0     0
    73  73693  7100     0     0     0     0     0     0
    74  80793     0     0     0     0     0     0     0
    75  77528  3007   258     0     0     0     0     0
    76  34898 34767 10611   517     0     0     0     0
    77  54795 21256  4119   623     0     0     0     0
    78  57551 21264  1978     0     0     0     0     0
    79  46958 28434  5401     0     0     0     0     0
    80  46392 30479  3922     0     0     0     0     0
    81  80793     0     0     0     0     0     0     0
    82  72152  8641     0     0     0     0     0     0
    83  57727 21903  1163     0     0     0     0     0
    84  73059  7734     0     0     0     0     0     0
    85  78106  2687     0     0     0     0     0     0
    86  64837 15956     0     0     0     0     0     0
    87  75894  4487   412     0     0     0     0     0
    88  77086  3707     0     0     0     0     0     0
    89  48322 32471     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0
    91  36640 30722 11104  1868   459     0     0     0
    92  54507 24631  1655     0     0     0     0     0
    93  73941  6852     0     0     0     0     0     0
    94  69270 10409  1114     0     0     0     0     0
    95  43208 20297 14643  2645     0     0     0     0
    96  65012 15781     0     0     0     0     0     0
    97  75424  5369     0     0     0     0     0     0
    98  65965 13006  1822     0     0     0     0     0
    99  78627  2166     0     0     0     0     0     0
    100 52399 15984  9607  2803     0     0     0     0

Beta Diversity

Principal Components Analysis

(For the desktop, the amount of data we generated is too much, so we need to reduce the amount of data we use. To do so we average over time blocks, here of length 100.)

AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
    ) %>% dplyr::summarise(
      dplyr::across(.fns = ~ mean(.x))
      )

We can perform a PCA and see if are data can be summarised by a small number of dimensions. As we shall see, constraining to the first 25 principal components does not harm the system much.

PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)

There is not actually a lot of dependence within the system it appears. Consider the amount of variance explained by the first six principal components (ordered, as is tradition, by amount of variation explained).

head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.03749 0.03273 0.03095 0.02751 0.02501 0.02482 

The amount of explained variation is 0.99994. The traditional biplot follows, but despite the seeming presence of patterns, so little of the variance is explained that is probably not worth further examination.

ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

We can try again with presence-absence data instead.

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.05538 0.04585 0.04484 0.03901 0.03578 0.03410 

So it is a bit better, but not by much.

ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

I should note that this is not a failure of the method persay. What this says to me is that dimension reduction of the system cannot reduce the system to a human-readable set of descriptors. The system is still being reduced (from 100 Species * 10 Environments to 25 Components to cover 0.99994 of the variation). My initial hypothesis for when the systems are coupled is that, as coupling increases, the number of principal components should decrease, since one system’s changes will be better predictors of the next system’s changes. (An interesting related question; do the number of principal components correlate with the amount of biodiversity in the system?

ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time

Since trying to reduce the system dimensionally does not work (well enough) a next attempt might be to consider how the pair-wise beta diversity changes over the course of the simulation. Initial problems include that there are quite a few measures of beta diversity as well as that the (square of the) number of environments will determine how many entries we need to consider.

Cluster Analysis Over Time

One perhaps interesting idea is to try to group the environments by cluster by considering the abundances (or presence-absence) of the species present as traits. The question then is whether the environments have a tendency to attract to specific points or if they wander around each other without relation. (The latter is more neutral.) If they appear to be convergent (which so far would seem to disagree mostly with the alpha diversity analyses), then that would imply that dynamics determine the majority of the system’s fate, while if they instead wander more randomly, then they would appear to be dominated by the neutral mechanisms. (Of course, this is affected by the rate of the neutral mechanisms, so it exists on a definite continuum.) (Note, of course, that cluster analysis usually includes something like PCA, so we would not necessarily expect a different result.)

State Network

An additional idea that might be worth an initial look over is whether we can create the subspace of the graph explored by the simulation set. This is probably of the lowest priority of the things described above, but this is of mathematical interest and does have some relationship to diversity measures. Such a map would usually be visualised with columns representing richness and edges representing moves along the system. We can colour the edges to represent whether they were via arrival, dynamic extinction, or neutral extinction as well.

LS0tDQp0aXRsZTogIk11bHRpcGxlIE51bWVyaWNhbCBBc3NlbWJseSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQojIFNlY3Rpb25zIHsudGFic2V0fQ0KDQojIyBBYnN0cmFjdA0KDQpJbiB0aGlzIGRvY3VtZW50LCBJIHdhbnQgdG8gcHJvdmlkZSBhIHdyaXRlLXVwIG9mIGhvdyB0aGlzIG1vZGVsIGlzIHNpbWlsYXIgdG8gYW5kIGRpZmZlcmVudCBmcm9tIHRoZSB2ZXJzaW9ucyBwcmV2aW91c2x5IGltcGxlbWVudGVkLg0KVGhpcyBtb2RlbCBpcyBtZWFudCB0byBwcm92aWRlIGFzc2VtYmx5IG9mIG11bHRpcGxlIHNpdGVzIGF0IHRoZSBzYW1lIHRpbWUsIHRoYXQgbWF5LCBvciBtYXkgbm90LCBiZSBjb25uZWN0ZWQgaW4gc29tZSBmYXNoaW9uLiANCkFsb25nIHRoZSB3YXksIEkgd2lsbCBtZW50aW9uIHNvbWUgb2YgdGhlIGlucHV0IGFuZCBvdXRwdXQgZm9ybWF0IHRoYXQgSSBhbSBleHBlY3QgYXMgZG9jdW1lbnRhdGlvbi4NCkF0IHRoZSBlbmQsIEkgd2lsbCBiZSBwcmVzZW50aW5nIHNvbWUgcHJlbGltaW5hcnkgcmVzdWx0cy4NCkkgZXNwZWNpYWxseSB3YW50IHRvIHVzZSB0aGlzIGFzIGEgdmVoaWNsZSB0byB0aGluayBhYm91dCBob3cgdG8gYW5hbHlzZSB0aG9zZSByZXN1bHRzLg0KDQojIyBGdW5jdGlvbnMNCg0KIyMgUmVzdWx0cywgTm8gU3BhdGlhbCBTdHJ1Y3R1cmUgey50YWJzZXR9DQoNCkZpcnN0LCB3ZSBsb2FkIHVwIHRoZSBwcmVsaW1pbmFyeSByZXN1bHRzLg0KSW4gdGhpcyBjYXNlLCB3ZSBhcmUgbG9hZGluZyBhIHN5c3RlbSBpbiB3aGljaCANCjM0IGJhc2FsIHNwZWNpZXMgYW5kIDY2IGNvbnN1bWVyIHNwZWNpZXMgZm9ybSB0aGUgcG9vbCBmb3IgMTAgdW5jb25uZWN0ZWQgZW52aXJvbm1lbnRzLg0KVGhlIHBvb2wgYW5kIGludGVyYWN0aW9uIG1hdHJpeCB3ZXJlIGFzc2VtYmxlZCB3aXRoIHRoZSBkZWZhdWx0IHBhcmFtZXRlcnMgZnJvbSBMYXcgYW5kIE1vcnRvbidzIDE5OTYgd29yay4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdnZm9ydGlmeSkgIyB1c2VkIGZvciBiaXBsb3RzIG9mIFBDQXMNCiMgbGlicmFyeShSTVRSQ29kZTIpDQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQtUmVzdWx0LUVudjEwLU5vbmUuUkRhdGEiKQ0KKQ0KDQojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS83MTcyODMyDQppZnJtIDwtIGZ1bmN0aW9uKG9iaiwgZW52ID0gZ2xvYmFsZW52KCkpIHsNCiAgICBvYmogPC0gZGVwYXJzZShzdWJzdGl0dXRlKG9iaikpDQogICAgaWYoZXhpc3RzKG9iaiwgZW52aXIgPSBlbnYpKSB7DQogICAgICAgIHJtKGxpc3QgPSBvYmosIGVudmlyID0gZW52KQ0KICAgIH0NCn0NCmBgYA0KDQojIyMgRXZlbnRzDQpJbiB0b3RhbCwgYHIgbnJvdyhyZXN1bHQkRXZlbnRzKWAgZXZlbnRzIHdlcmUgdXNlZCBpbiB0aGVzZSBlbnZpcm9ubWVudHMsIHdpdGggdGhlIHNwZWNpZXMgYW5kIGVudmlyb25tZW50IGludmFzaW9uIGJvdGggcmFuZG9tbHkgYXNzaWduZWQuDQpUaGUgbnVtYmVyIG9mIGFycml2YWwgYW5kIGV4dGluY3Rpb24gZXZlbnRzIHdlcmUgY29udHJvbGxlZCB0byBib3RoIGJlIGhhbGYgb2YgdGhpcyBudW1iZXIuDQpXZSBjaG9zZSB0aGlzIG51bWJlciBkdWUgdG8gdGhlIGNvdXBvbiBjb2xsZWN0aW5nIHByb2JsZW0uDQpJbiBwYXJ0aWN1bGFyLCB3ZSB1c2UgdGhlIHJlc3VsdCB0aGF0IFt0aGUgcHJvYmFiaWxpdHkgb2YgZW5jb3VudGVyaW5nIGVhY2ggc3BlY2llcyBpcyBib3VuZGVkOl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ291cG9uX2NvbGxlY3RvciUyN3NfcHJvYmxlbSNFeHRlbnNpb25zX2FuZF9nZW5lcmFsaXphdGlvbnMpDQokJFx0ZXh0e1ByfShcdGV4dHtEcmF3c30gPCBuIFxsb2dfe2V9IG4gKyBjIG4pIFxyaWdodGFycm93IFxleHAoLVxleHAoLWMpKSBcdGV4dHsgYXMgfSBuIFxyaWdodGFycm93IFxpbmZ0eSQkDQp3aGVyZSAkbiQgaXMgdGhlIG51bWJlciBvZiBzcGVjaWVzIGFuZCAkYyQgaXMgYSBjb25zdGFudC4NCkZvciBvdXIgcHVycG9zZXMsIHdlIGNob29zZSAkYyA9IDUkIHNvIHRoYXQgd2UgaGF2ZSBhIHByb2JhYmlsaXR5IG9mIGFib3V0ICQ5OS4zXCUkIG9mIHNlZWluZyBlYWNoIHNwZWNpZXMgaW4gZWFjaCBlbnZpcm9ubWVudC4gDQpJbiBwcmFjdGljZSwgd2UgZmFpbGVkIHRvIG9ic2VydmUgYHIgc3VtKHdpdGgocmVzdWx0JEV2ZW50cywgdGFibGUoU3BlY2llcywgRW52aXJvbm1lbnQpKSA9PSAwKWAgc3BlY2llcy1lbnZpcm9ubWVudCBjb21iaW5hdGlvbnMuDQpOb3RhYmx5LCBuZWFybHkgZXZlcnkgc3BlY2llcyBoYWQgYXQgbGVhc3Qgb25lIHN1Y2Nlc3NmdWwgaW52YXNpb247DQpgciBzdW0ocm93U3Vtcyh3aXRoKHJlc3VsdCRFdmVudHMgJT4lIGZpbHRlcihUeXBlID09ICJBcnJpdmFsIiksIHRhYmxlKFNwZWNpZXMsIEVudmlyb25tZW50LCBTdWNjZXNzKSlbLCAsIDJdKSA9PSAwKWAgZGlkIG5vdC4NCg0KVGhlIGluaXRpYWwgYWJ1bmRhbmNlIHdhcyBzZXQgdG8gYmUgNDAwMCB0aW1lcyB0aGUgZWxpbWluYXRpb24gdGhyZXNob2xkLCBpbiBsaW5lIHdpdGggd29yayBvbiBtaW5pbXVtIHZpYWJsZSBwb3B1bGF0aW9ucyAoVHJhaWxsIGV0IGFsLiAyMDA3KS4NClRoZSBlbGltaW5hdGlvbiB0aHJlc2hvbGQgaXMgYWRtaXR0ZWRseSBtb3JlIGFyYml0cmFyeSwgc2luY2UgaXQgc2V0cyBhbiBlZmZlY3RpdmUgaW5kaXZpZHVhbC1hcmVhIHJlbGF0aW9uc2hpcC4NCkZvciB0aGlzIGNhbGN1bGF0aW9uLCBJIHNldCBpdCB0byAkMTBeey00fSQsIGluIGxpbmUgd2l0aCBvdXIgcHJldmlvdXMgY2FsY3VsYXRpb25zLg0KVGhpcyBpcyBsYXJnZSBlbm91Z2ggdG8gYXZvaWQgbnVtZXJpY2FsIGRpZmZpY3VsdGllcyBmcm9tIHByZWNpc2lvbiwgd2hpbGUgYmVpbmcgbG93IGVub3VnaCB0byByZXByZXNlbnQgYSBkZWNlbnQgc2l6ZWQgcmVnaW9uLg0KDQpTaW5jZSBlYWNoIHBvcHVsYXRpb24gaXMgYXNzZW1ibGluZyBzaW11bHRhbmVvdXNseSwgSSBjaG9zZSB0byB1c2UgZXhwb25lbnRpYWwgd2FpdGluZyB0aW1lcyBmb3IgdGhlIGludGVyLXNwZWNpZXMgYXJyaXZhbCBhbmQgZXh0aW5jdGlvbiB0aW1lcy4NCk5vdGUgdGhhdCB0aGVzZSByYXRlcyBhcmUgc2hhcmVkIGJldHdlZW4gc3BlY2llcyBhbmQgZW52aXJvbm1lbnRzLCBidXQgYXJyaXZhbCBhbmQgZXh0aW5jdGlvbiBhcmUgZnVsbHkgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4NClNwZWNpZXMgYW5kIGVudmlyb25tZW50IGFmZmVjdGVkIGluIGVhY2ggZXZlbnQgd2FzIGNob3NlbiB1bmlmb3JtbHkgYXQgcmFuZG9tLg0KVGhlIHF1ZXN0aW9uIHRoZW4gaXMgaG93IHRvIHNldCB0aGUgcmF0ZS4NCg0KVG8gc2V0IHRoZSByYXRlIGluIHRoaXMgY2FzZSwgSSBjaG9zZSB0byBzZXQgaXQgdG8gdGhlIGxhcmdlc3QgZWlnZW52YWx1ZSBtYWduaXR1ZGUgb2YgdGhlIHBlci1lbnZpcm9ubWVudCBpbnRlcmFjdGlvbiBtYXRyaWNlcy4NClRoaXMgbWFnbml0dWRlIGNvcnJlc3BvbmRzIHRvIHRoZSBzdHJvbmdlc3QgcmVzcG9uc2Ugd2UgY2FuIHNlZSBmcm9tIHRoZSBpbnRlcmFjdGlvbiBtYXRyaXggYW5kLCBpZiB0aGUgaW50ZXJhY3Rpb24gbWF0cml4IGlzIGEgZ29vZCBhcHByb3hpbWF0aW9uIGZvciB0aGUgSmFjb2JpYW4gYXJvdW5kIGEgc3RhYmxlIGZpeGVkIHBvaW50ICh3aGljaCBpcyBub3QgZ3VhcmFudGVlZCksIGluZGljYXRlcyB0aGUgY2hhcmFjdGVyaXN0aWMgdGltZSBzY2FsZSBvZiB0aGUgZGVjYXkgdG8gZXF1aWxpYnJpdW0uDQpIZW5jZSwgKG92ZXJhbGwpIGFycml2YWwgcmF0ZXMgYW5kIChvdmVyYWxsKSBleHRpbmN0aW9uIHJhdGVzIHNob3VsZCBoYXBwZW4gb24gdGhlIHNhbWUgdGltZXNjYWxlIGFzIHRoZSAobGFyZ2VzdCkgZHluYW1pY3MgaW4gdGhlIHN5c3RlbS4NClNpbmNlIHRoZXJlIGFyZSAxMCBlbnZpcm9ubWVudHMsIHdlIHNob3VsZCB0aGVuIGV4cGVjdCB0aGF0IDEwIGNoYXJhY3RlcmlzdGljIHRpbWUgc2NhbGVzLCBvbiBhdmVyYWdlLCBzaG91bGQgb2NjdXIgaW4gYmV0d2VlbiBhcnJpdmFsIGV2ZW50cyBpbiB0aGUgc2FtZSBlbnZpcm9ubWVudC4NCg0KIyMjIEFidW5kYW5jZSB7LnRhYnNldH0NCg0KV2l0aCAxMCBlbnZpcm9ubWVudHMsIGl0IGlzIHByb2JhYmx5IG5vdCBoZWxwZnVsIHRvIGNoZWNrIDEwIGluZGl2aWR1YWwgYWJ1bmRhbmNlIGN1cnZlcywgYnV0IGxvb2tpbmcgYXQgdGhlIGZpcnN0IG9uZSBtaWdodCBiZSBoZWxwZnVsLg0KYGBge3J9DQpMYXdNb3J0b24xOTk2X1Bsb3RBYnVuZGFuY2UocmVzdWx0JEFidW5kYW5jZVssIGMoMSwgMjoxMDEpXSkgLT4gb2JqOw0KYGBgDQpgYGB7cn0NCm9iaiArIGdncGxvdDI6OnNjYWxlX3lfbG9nMTAoKSArIGdncGxvdDI6Omd1aWRlcyhjb2xvciA9IEZBTFNFKQ0KYGBgDQpFdmVyeSB2ZXJ0aWNhbCBsaW5lIGlzIGEgc3BlY2llcyBpbnRyb2R1Y3Rpb24gb3IgZXh0aW5jdGlvbiBieSBuZXV0cmFsIGR5bmFtaWNzLg0KDQojIyMjIEFscGhhIERpdmVyc2l0eSBwbG90cyB7LnRhYnNldH0NCg0KIyMjIyMgSW50cm8NClBlcmhhcHMgbW9yZSBpbnRyaWd1aW5nIG1pZ2h0IGJlIHNvbWUgc2Vuc2Ugb2YgdGhlIGJpb2RpdmVyc2l0eSB0aGF0IHdlIGhhdmUgaW4gZWFjaCBzeXN0ZW0uDQpXZSBicmVhayB0aGUgYWJ1bmRhbmNlIHJlc3VsdHMgdXAgYnkgZW52aXJvbm1lbnQsIHRoZW4gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2Ygbm9uLXplcm8gYWJ1bmRhbmNlIGN1cnZlcyBhdCBlYWNoIHRpbWUgcG9pbnQuDQpXZSBhbHNvIGNhbGN1bGF0ZSB0aGUgU2hhbm5vbiBlbnRyb3B5IChyZW1pbmRlcjogaGlnaGVyIGVudHJvcHkgbWVhbnMgbW9yZSB1bmNlcnRhaW50eSB3aGljaCBtZWFucyBhIGZsYXR0ZXIgZGlzdHJpYnV0aW9uKS4NCmBgYHtyfQ0KRGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgIGVudHJvcHkgPC0gZW52IC8gYWJ1bmRTdW0NCiAgICBlbnRyb3B5IDwtIC0gYXBwbHkoDQogICAgICBlbnRyb3B5LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBzdW0oaWZlbHNlKHggIT0gMCwgeCAqIGxvZyh4KSwgMCkpDQogICAgICB9KQ0KICAgIHNwZWNpZXMgPC0gYXBwbHkoDQogICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgIH0NCiAgICApDQogICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgIEVudHJvcHkgPSBlbnRyb3B5LA0KICAgICAgICAgICAgICAgU3BlY2llcyA9IHNwZWNpZXMsDQogICAgICAgICAgICAgICBFbnZpcm9ubWVudCA9IGkpDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCg0KRGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRGl2ZXJzaXR5KQ0KRGl2ZXJzaXR5IDwtIERpdmVyc2l0eSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgRXZlbm5lc3MgPSBFbnRyb3B5IC8gbG9nKFJpY2huZXNzKQ0KKQ0KYGBgDQoNCg0KIyMjIyMgUmljaG5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBSaWNobmVzcyA9IG1lYW4oUmljaG5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gUmljaG5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANClNvIHJpY2huZXNzIGhvdmVycyBhcm91bmQgYSBzaW1pbGFyIHJlZ2ltZSB0aHJvdWdob3V0IHRoZSBtYWpvcml0eSBvZiB0aGUgc2ltdWxhdGlvbi4NCk5vdGUgaW4gdGhpcyBwbG90IHRoYXQgd2UgaGF2ZSBlbXBoYXNpc2VkIG9uZSBlbnZpcm9ubWVudGFsIGN1cnZlIGFuZCBzdXBlcmltcG9zZWQgdGhlIG1lYW4gaW4gYmxhY2suDQpXZSBkbyBtYW5hZ2UgdG8gcmVhY2ggaGVpZ2h0cyBvZiAxMSBzcGVjaWVzIGluIG9uZSBlbnZpcm9ubWVudCwgYnV0IHRoZXNlIGhlaWdodHMgYXJlIHNob3J0bGl2ZWQuDQpJbnN0ZWFkLCB3ZSBzZWVtIHRvIG9ic2VydmUgYSAodGltZSBhbmQgZW52aXJvbm1lbnQgYXZlcmFnZWQpIHZhbHVlIG9mIA0KYHIgbWVhbihEaXZlcnNpdHkkUmljaG5lc3MpYC4NCihJZiB3ZSBjb25zaWRlciB0aGUgZmlyc3QgMTAsMDAwIHRpbWUgdW5pdHMgYXMgYnVybi1pbiwgd2UgaW5zdGVhZCBzZWUgYSB2YWx1ZSBvZg0KYHIgbWVhbihEaXZlcnNpdHkkUmljaG5lc3NbRGl2ZXJzaXR5JFRpbWUgPiAxMDAwMF0pYC4pDQoNCiMjIyMjIEVudHJvcHkNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHksDQogICAgY29sb3IgPSBmYWN0b3IoRW52aXJvbm1lbnQpLA0KICAgIGFscGhhID0gaWZlbHNlKEVudmlyb25tZW50ID09IDEsIDEsIDAuMykNCiAgKQ0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQogIGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICBUaW1lDQogICAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICAgIEVudHJvcHkgPSBtZWFuKEVudHJvcHkpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRW50cm9weQ0KICApLA0KICBjb2xvciA9ICJibGFjayIsDQogIGluaGVyaXQuYWVzID0gRkFMU0UNCikgKyBnZ3Bsb3QyOjpndWlkZXMoDQogIGFscGhhID0gIm5vbmUiDQopICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZGlzY3JldGUoDQogICJFbnZpcm9ubWVudCINCikNCmBgYA0KRW50cm9weSBoYXMgYSBzaW1pbGFyLCBidXQgaGlnaGx5IGVycmF0aWMsIGJlaGF2aW91ci4NCklmIG9uZSB0cmllcyB0byBmb2xsb3cgb25lIG9mIHRoZSBlbnRyb3B5IGN1cnZlcywgdGhlbiBvbmUgc2VlcyB0aGF0IHRoZXkgaGF2ZQ0KZmFpcmx5IHN1YnN0YW50aWFsIHBlcmlvZHMgb2YgYWxtb3N0IHNtb290aCBiZWhhdmlvdXIgZm9sbG93ZWQgYnkgc3VkZGVubHkNCnZlcnkgbm9pc3kgYmVoYXZpb3VyLCBhbmQgbm9pc2Ugc2VlbXMgdG8gYmUgdGhlIGRvbWluYW50IG1vZGUgaWYgb25lIHRyaWVzDQp0byBleGFtaW5lIHRoZSBsb3cgYWxwaGEgZW52aXJvbm1lbnQgaW4gdGhlIGJhY2tncm91bmQuDQpUaGVyZSBhcmUgc29tZSBlYXN5IHRvIG1ha2UgcHJlZGljdGlvbnMuDQpFeHRpbmN0aW9ucyByZWR1Y2UgdGhlIGVudHJvcHkgaW4gdGhlIHN5c3RlbSwgYXMgeW91IGJlY29tZSBtb3JlIGNlcnRhaW4gYWJvdXQNCndoYXQgcmVtYWlucy4NCkFuYWxvZ291c2x5LCBhcnJpdmFscyBpbmNyZWFzZSB0aGUgZW50cm9weS4NCldlIGNhbiBwcm9iYWJseSBiZXR0ZXIgc2VlIHRoZSByZWxhdGlvbnNoaXAgYmV5b25kIHRoZXNlIHByaW5jaXBsZXMgYnkgcGxvdHRpbmcNCmVudHJvcHkgYWdhaW5zdCByaWNobmVzcyBhbmQgY29ubmVjdGluZyBvYnNlcnZhdGlvbnMgYnkgZW52aXJvbm1lbnQgYW5kIHRpbWUuDQoNCiMjIyMjIEVudHJvcHktUmljaG5lc3MNCmBgYHtyfQ0KIyBnZ3Bsb3QyOjpnZ3Bsb3QoDQojICAgRGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKEVudmlyb25tZW50IDwgNCksIA0KIyAgIGdncGxvdDI6OmFlcygNCiMgICAgIHggPSBSaWNobmVzcywNCiMgICAgIHkgPSBFbnRyb3B5LA0KIyAgICAgZ3JvdXAgPSBFbnZpcm9ubWVudCwNCiMgICAgIGNvbG9yID0gVGltZQ0KIyAgICkNCiMgKSArIGdncGxvdDI6Omdlb21fcGF0aCgNCiMgKSArIGdncGxvdDI6Omd1aWRlcygNCiMgICBhbHBoYSA9ICJub25lIg0KIyApDQoNCnBsb3RseTo6cGxvdF9seShkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKEVudmlyb25tZW50IDwgMiksDQogICAgICAgICAgICAgICAgeCA9IH5SaWNobmVzcywgeSA9IH5FbnRyb3B5LCB6ID0gflRpbWUsIHR5cGUgPSAic2NhdHRlcjNkIiwNCiAgICAgICAgICAgICAgICBtb2RlID0gImxpbmVzIiwgb3BhY2l0eSA9IDEsIGxpbmUgPSBsaXN0KGNvbG9yID0gflRpbWUpKQ0KYGBgDQpJdCBzZWVtcyBxdWl0ZSBoYXJkIHRvIHRlbGwsIGJ1dCB0aGVyZSBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgYW55IHBhcnRpY3VsYXIgb3JpZW50YXRpb24gKGNsb2Nrd2lzZSwgY291bnRlci1jbG9ja3dpc2UpIG9yIHNpbWlsYXIgcGF0dGVybiBoZXJlLg0KDQojIyMjIyBFdmVubmVzcw0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIERpdmVyc2l0eSwgDQogIGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MsDQogICAgY29sb3IgPSBmYWN0b3IoRW52aXJvbm1lbnQpLA0KICAgIGFscGhhID0gaWZlbHNlKEVudmlyb25tZW50ID09IDEsIDEsIDAuMykNCiAgKQ0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQogIGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICBUaW1lDQogICAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICAgIEV2ZW5uZXNzID0gbWVhbihFdmVubmVzcykNCiAgICApLA0KICBtYXBwaW5nID0gZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFdmVubmVzcw0KICApLA0KICBjb2xvciA9ICJibGFjayIsDQogIGluaGVyaXQuYWVzID0gRkFMU0UNCikgKyBnZ3Bsb3QyOjpndWlkZXMoDQogIGFscGhhID0gIm5vbmUiDQopICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZGlzY3JldGUoDQogICJFbnZpcm9ubWVudCINCikNCmBgYA0KRXZlbm5lc3MgaGVscHMgaGlnaGxpZ2h0IHRoYXQgdGhpcyBpcyBhIGhpZ2ggdmFyaWFuY2UgcHJvY2VzcyBidXQgd2l0aCBhIHJlbGF0aXZlbHkgY29uc3RyYWluZWQgbWVhbi4NCg0KIyMjIyMgRW52aXJvbm1lbnQgRGl2ZXJzaXR5DQpXZSBjYW4sIG9mIGNvdXJzZSwgZmxpcCB0aGUgaWRlYSBvbiBpdHMgaGVhZC4NCkluc3RlYWQgb2YgZXhhbWluaW5nIHRoZSBkaXZlcnNpdHkgb2Ygc3BlY2llcyB3aXRoaW4gZW52aXJvbm1lbnRzLCB3ZSBjYW4NCmxvb2sgYXQgdGhlIGRpdmVyc2l0eSBvZiBlbnZpcm9ubWVudHMgb2NjdXBpZWQgYnkgc3BlY2llcy4NClNpbmNlIHZlcnkgZmV3IHNwZWNpZXMgZW5kIHVwIG9jY3VweWluZyBlbnZpcm9ubWVudHMsIHdlIGp1c3QgbG9vayBhdCByaWNobmVzcy4NClVuZm9ydHVuYXRlbHksIHRoaXMgaXMgcXVpdGUgYSBtZW1vcnkgZXhoYXVzdGl2ZSB0YXNrLg0KDQpgYGB7cn0NCkVudkRpdmVyc2l0eSA8LSBsYXBwbHkoDQogICAgMTooKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMpLA0KICAgIGZ1bmN0aW9uKGksIGFidW5kLCBudW1TcGVjaWVzKSB7DQogICAgICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgICAgICBlbnYgPC0gYWJ1bmRbLCAxICsgaSArIG51bVNwZWNpZXMgKiAoMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzIC0gMSldDQogICAgICAgIHJpY2huZXNzIDwtIHJvd1N1bXMoZW52ICE9IDApDQogICAgICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgICAgICBlbnZpcm9ubWVudHMgPC0gYXBwbHkoDQogICAgICAgICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgICAgICAgICAgdG9TdHJpbmcod2hpY2goeCA+IDApKQ0KICAgICAgICAgICAgfQ0KICAgICAgICApDQogICAgICAgIGRhdGEuZnJhbWUoVGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgICAgIFJpY2huZXNzID0gcmljaG5lc3MsIA0KICAgICAgICAgICAgICAgICAgIEFidW5kYW5jZSA9IGFidW5kU3VtLA0KICAgICAgICAgICAgICAgICAgIFNwZWNpZXMgPSBpLA0KICAgICAgICAgICAgICAgICAgIEVudmlyb25tZW50cyA9IGVudmlyb25tZW50cykNCiAgICB9LA0KICAgIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgICBudW1TcGVjaWVzID0gKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMNCikNCg0KRW52RGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRW52RGl2ZXJzaXR5KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBFbnZEaXZlcnNpdHkgJT4lIGRwbHlyOjpmaWx0ZXIoUmljaG5lc3MgPiAxKSwgDQogIGFlcyh4ID0gVGltZSwgeSA9IFJpY2huZXNzLCBjb2xvciA9IFNwZWNpZXMpDQopICsgZ2VvbV9wb2ludCgNCiAgYWxwaGEgPSAwLjAxLCBzaXplID0gMw0KKSArIGd1aWRlcygNCiAgY29sb3IgPSAibm9uZSINCikNCmBgYA0KDQpPbmUgaW1tZWRpYXRlbHkgaW50ZXJlc3RpbmcgdHJlbmQgaGVyZSBpcyB0aGF0IHZlcnkgZmV3IHNwZWNpZXMgYXJlIHByZXNlbnQNCmFjcm9zcyBtb3JlIHRoYW4gNSBlbnZpcm9ubWVudHMgYXQgYSBnaXZlbiB0aW1lLg0KSW5kZWVkLCBvbmx5IGByIHVuaXF1ZShFbnZEaXZlcnNpdHkkU3BlY2llc1tFbnZEaXZlcnNpdHkkUmljaG5lc3MgPiA1XSlgIGFyZSANCmV2ZXIgcHJlc2VudCBpbiBtb3JlIHRoYW4gNSBlbnZpcm9ubWVudHMgYXQgb25jZS4NCldlIGNhbiBhbHNvIGV4YW1pbmUgaG93IGxvbmcgdGhlc2UgcGVyaW9kcyBvY2N1ciBmb3IgYnkgc3BlY2llcyBieSB0YWJ1bGF0aW9uLg0KV2UgYmxvY2sgdGltZXMgc28gdGhhdCBlbnRyaWVzIGFyZSBhbGwgb2YgdGhlIHNhbWUgdW5pdCBsZW5ndGgsIGFuZCByb3VuZCB0aGUNCmF2ZXJhZ2UgcmljaG5lc3MgZHVyaW5nIHRoZSBnaXZlbiB0aW1lIHVuaXQuDQoNCmBgYHtyfQ0Kd2l0aChFbnZEaXZlcnNpdHkgJT4lIG11dGF0ZSgNCiAgVGltZSA9IGZsb29yKFRpbWUpDQogICkgJT4lIGdyb3VwX2J5KA0KICAgIFRpbWUsIFNwZWNpZXMNCiAgICApICU+JSBzdW1tYXJpc2UoDQogICAgICBSaWNobmVzcyA9IHJvdW5kKG1lYW4oUmljaG5lc3MpKSwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICAgICksDQogICAgIHRhYmxlKFNwZWNpZXMsIFJpY2huZXNzKSkNCmBgYA0KIyMjIyBCZXRhIERpdmVyc2l0eSB7LnRhYnNldH0NCg0KIyMjIyMgUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMNCihGb3IgdGhlIGRlc2t0b3AsIHRoZSBhbW91bnQgb2YgZGF0YSB3ZSBnZW5lcmF0ZWQgaXMgdG9vIG11Y2gsIHNvIHdlIG5lZWQgdG8NCnJlZHVjZSB0aGUgYW1vdW50IG9mIGRhdGEgd2UgdXNlLg0KVG8gZG8gc28gd2UgYXZlcmFnZSBvdmVyIHRpbWUgYmxvY2tzLCBoZXJlIG9mIGxlbmd0aCAxMDAuKQ0KYGBge3J9DQpBdmVyYWdlZEFidW5kYW5jZSA8LSByZXN1bHQkQWJ1bmRhbmNlICU+JSBkYXRhLmZyYW1lKA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgdGltZSA9IGZsb29yKHRpbWUvMTAwKSoxMDANCiAgKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIHRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCiAgICAgICkNCmBgYA0KDQpXZSBjYW4gcGVyZm9ybSBhIFBDQSBhbmQgc2VlIGlmIGFyZSBkYXRhIGNhbiBiZSBzdW1tYXJpc2VkIGJ5IGEgc21hbGwgbnVtYmVyIG9mDQpkaW1lbnNpb25zLg0KQXMgd2Ugc2hhbGwgc2VlLCBjb25zdHJhaW5pbmcgdG8gdGhlIGZpcnN0IDI1IHByaW5jaXBhbCBjb21wb25lbnRzIGRvZXMgbm90DQpoYXJtIHRoZSBzeXN0ZW0gbXVjaC4NCmBgYHtyfQ0KUENBIDwtIHByY29tcChBdmVyYWdlZEFidW5kYW5jZSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLCANCiAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSwgcmFuay4gPSAyNSkNCmBgYA0KDQpUaGVyZSBpcyBub3QgYWN0dWFsbHkgYSBsb3Qgb2YgZGVwZW5kZW5jZSB3aXRoaW4gdGhlIHN5c3RlbSBpdCBhcHBlYXJzLg0KQ29uc2lkZXIgdGhlIGFtb3VudCBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgdGhlIGZpcnN0IHNpeCBwcmluY2lwYWwgY29tcG9uZW50cw0KKG9yZGVyZWQsIGFzIGlzIHRyYWRpdGlvbiwgYnkgYW1vdW50IG9mIHZhcmlhdGlvbiBleHBsYWluZWQpLg0KDQpgYGB7cn0NCmhlYWQoc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KVGhlIGFtb3VudCBvZiBleHBsYWluZWQgdmFyaWF0aW9uIGlzIGByIHN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKWAuDQpUaGUgdHJhZGl0aW9uYWwgYmlwbG90IGZvbGxvd3MsIGJ1dCBkZXNwaXRlIHRoZSBzZWVtaW5nIHByZXNlbmNlIG9mIHBhdHRlcm5zLA0Kc28gbGl0dGxlIG9mIHRoZSB2YXJpYW5jZSBpcyBleHBsYWluZWQgdGhhdCBpcyBwcm9iYWJseSBub3Qgd29ydGggZnVydGhlcg0KZXhhbWluYXRpb24uDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6YXV0b3Bsb3QoUENBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkQWJ1bmRhbmNlICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSAidGltZSIpDQpgYGANCg0KV2UgY2FuIHRyeSBhZ2FpbiB3aXRoIHByZXNlbmNlLWFic2VuY2UgZGF0YSBpbnN0ZWFkLg0KYGBge3J9DQpBdmVyYWdlZFBBIDwtIHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgZHBseXI6OmFjcm9zcyguY29scyA9ICF0aW1lLCAuZm5zID0gfiAueCA+IDApDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KYGBge3J9DQpQQ0FQQSA8LSBwcmNvbXAoQXZlcmFnZWRQQSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLCANCiAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSwgcmFuay4gPSAyNSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoc3VtbWFyeShQQ0FQQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQpTbyBpdCBpcyBhIGJpdCBiZXR0ZXIsIGJ1dCBub3QgYnkgbXVjaC4NCg0KYGBge3J9DQpnZ3Bsb3QyOjphdXRvcGxvdChQQ0FQQSwgbG9hZGluZ3MgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBBdmVyYWdlZFBBICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSAidGltZSIpDQpgYGANCg0KSSBzaG91bGQgbm90ZSB0aGF0IHRoaXMgaXMgbm90IGEgZmFpbHVyZSBvZiB0aGUgbWV0aG9kIHBlcnNheS4gDQpXaGF0IHRoaXMgc2F5cyB0byBtZSBpcyB0aGF0IGRpbWVuc2lvbiByZWR1Y3Rpb24gb2YgdGhlIHN5c3RlbSBjYW5ub3QgcmVkdWNlIHRoZQ0Kc3lzdGVtIHRvIGEgaHVtYW4tcmVhZGFibGUgc2V0IG9mIGRlc2NyaXB0b3JzLg0KVGhlIHN5c3RlbSBpcyBzdGlsbCBiZWluZyByZWR1Y2VkIChmcm9tIDEwMCBTcGVjaWVzICogMTAgRW52aXJvbm1lbnRzIHRvIDI1IENvbXBvbmVudHMgdG8gY292ZXIgYHIgc3VtKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pYCBvZiB0aGUgdmFyaWF0aW9uKS4NCjwhLS0gDQpJbnN0ZWFkLCBnaXZlbiB0aGUgbnVtYmVyIG9mIGVudmlyb25tZW50cyBhbmQgdGhlaXIgaW5kZXBlbmRlbmNlLCB0aGlzIHNlZW1zIHRvIA0Kc3VnZ2VzdCB0aGF0IHRoZSBtYWpvcml0eSBvZiB0aGUgY2hhbmdlIGluIGVudmlyb25tZW50cyBjYW4gYmUgZGVzY3JpYmVkIGJ5IGFib3V0IHR3byBjb21wb25lbnRzIHBsdXMgdGltZSBhbmQgc29tZSBtaW5vciB2YXJpYXRpb24gd2hlbiBjb25zaWRlcmluZyB0aGUgc3lzdGVtIGFzIGEgd2hvbGUuIA0KKFRoaXMgZG9lcyBub3Qgd29yayBmb3IgdGhlIGluZGl2aWR1YWwgc3lzdGVtcyBzaW5jZSB0aGVyZSBpcyByZWR1bmRhbmN5IHRoYXQgaXMgbG9zdC4pIC0tPg0KTXkgaW5pdGlhbCBoeXBvdGhlc2lzIGZvciB3aGVuIHRoZSBzeXN0ZW1zIGFyZSBjb3VwbGVkIGlzIHRoYXQsIGFzIGNvdXBsaW5nIGluY3JlYXNlcywgdGhlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyBzaG91bGQgZGVjcmVhc2UsIHNpbmNlIG9uZSBzeXN0ZW0ncyBjaGFuZ2VzIHdpbGwgYmUgYmV0dGVyIHByZWRpY3RvcnMgb2YgdGhlIG5leHQgc3lzdGVtJ3MgY2hhbmdlcy4NCihBbiBpbnRlcmVzdGluZyByZWxhdGVkIHF1ZXN0aW9uOyBkbyB0aGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIGNvcnJlbGF0ZSB3aXRoIHRoZSBhbW91bnQgb2YgYmlvZGl2ZXJzaXR5IGluIHRoZSBzeXN0ZW0/DQoNCmBgYHtyIGNsZWFudXB9DQppZnJtKEF2ZXJhZ2VkQWJ1bmRhbmNlKQ0KaWZybShBdmVyYWdlZFBBKQ0KaWZybShQQ0EpDQppZnJtKFBDQVBBKQ0KYGBgDQoNCiMjIyMjIEJldGEgRGl2ZXJzaXR5IE92ZXIgVGltZQ0KU2luY2UgdHJ5aW5nIHRvIHJlZHVjZSB0aGUgc3lzdGVtIGRpbWVuc2lvbmFsbHkgZG9lcyBub3Qgd29yayAod2VsbCBlbm91Z2gpIGENCm5leHQgYXR0ZW1wdCBtaWdodCBiZSB0byBjb25zaWRlciBob3cgdGhlIHBhaXItd2lzZSBiZXRhIGRpdmVyc2l0eSBjaGFuZ2VzIG92ZXINCnRoZSBjb3Vyc2Ugb2YgdGhlIHNpbXVsYXRpb24uDQpJbml0aWFsIHByb2JsZW1zIGluY2x1ZGUgdGhhdCB0aGVyZSBhcmUgcXVpdGUgYSBmZXcgbWVhc3VyZXMgb2YgYmV0YSBkaXZlcnNpdHkgDQphcyB3ZWxsIGFzIHRoYXQgdGhlIChzcXVhcmUgb2YgdGhlKSBudW1iZXIgb2YgZW52aXJvbm1lbnRzIHdpbGwgZGV0ZXJtaW5lIGhvdw0KbWFueSBlbnRyaWVzIHdlIG5lZWQgdG8gY29uc2lkZXIuDQoNCiMjIyMjIENsdXN0ZXIgQW5hbHlzaXMgT3ZlciBUaW1lDQpPbmUgcGVyaGFwcyBpbnRlcmVzdGluZyBpZGVhIGlzIHRvIHRyeSB0byBncm91cCB0aGUgZW52aXJvbm1lbnRzIGJ5IGNsdXN0ZXINCmJ5IGNvbnNpZGVyaW5nIHRoZSBhYnVuZGFuY2VzIChvciBwcmVzZW5jZS1hYnNlbmNlKSBvZiB0aGUgc3BlY2llcyBwcmVzZW50IGFzIHRyYWl0cy4NClRoZSBxdWVzdGlvbiB0aGVuIGlzIHdoZXRoZXIgdGhlIGVudmlyb25tZW50cyBoYXZlIGEgdGVuZGVuY3kgdG8gYXR0cmFjdCB0byANCnNwZWNpZmljIHBvaW50cyBvciBpZiB0aGV5IHdhbmRlciBhcm91bmQgZWFjaCBvdGhlciB3aXRob3V0IHJlbGF0aW9uLg0KKFRoZSBsYXR0ZXIgaXMgbW9yZSBuZXV0cmFsLikNCklmIHRoZXkgYXBwZWFyIHRvIGJlIGNvbnZlcmdlbnQgKHdoaWNoIHNvIGZhciB3b3VsZCBzZWVtIHRvIGRpc2FncmVlIG1vc3RseSB3aXRoDQp0aGUgYWxwaGEgZGl2ZXJzaXR5IGFuYWx5c2VzKSwgdGhlbiB0aGF0IHdvdWxkIGltcGx5IHRoYXQgZHluYW1pY3MgZGV0ZXJtaW5lIHRoZQ0KbWFqb3JpdHkgb2YgdGhlIHN5c3RlbSdzIGZhdGUsIHdoaWxlIGlmIHRoZXkgaW5zdGVhZCB3YW5kZXIgbW9yZSByYW5kb21seSwgdGhlbg0KdGhleSB3b3VsZCBhcHBlYXIgdG8gYmUgZG9taW5hdGVkIGJ5IHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMuDQooT2YgY291cnNlLCB0aGlzIGlzIGFmZmVjdGVkIGJ5IHRoZSByYXRlIG9mIHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMsIHNvIGl0IGV4aXN0cw0Kb24gYSBkZWZpbml0ZSBjb250aW51dW0uKQ0KKE5vdGUsIG9mIGNvdXJzZSwgdGhhdCBjbHVzdGVyIGFuYWx5c2lzIHVzdWFsbHkgaW5jbHVkZXMgc29tZXRoaW5nIGxpa2UgUENBLA0Kc28gd2Ugd291bGQgbm90IG5lY2Vzc2FyaWx5IGV4cGVjdCBhIGRpZmZlcmVudCByZXN1bHQuKQ0KDQojIyMjIyBTdGF0ZSBOZXR3b3JrDQpBbiBhZGRpdGlvbmFsIGlkZWEgdGhhdCBtaWdodCBiZSB3b3J0aCBhbiBpbml0aWFsIGxvb2sgb3ZlciBpcyB3aGV0aGVyIHdlIGNhbg0KY3JlYXRlIHRoZSBzdWJzcGFjZSBvZiB0aGUgZ3JhcGggZXhwbG9yZWQgYnkgdGhlIHNpbXVsYXRpb24gc2V0LiANClRoaXMgaXMgcHJvYmFibHkgb2YgdGhlIGxvd2VzdCBwcmlvcml0eSBvZiB0aGUgdGhpbmdzIGRlc2NyaWJlZCBhYm92ZSwgYnV0IHRoaXMNCmlzIG9mIG1hdGhlbWF0aWNhbCBpbnRlcmVzdCBhbmQgZG9lcyBoYXZlIHNvbWUgcmVsYXRpb25zaGlwIHRvIGRpdmVyc2l0eSANCm1lYXN1cmVzLg0KU3VjaCBhIG1hcCB3b3VsZCB1c3VhbGx5IGJlIHZpc3VhbGlzZWQgd2l0aCBjb2x1bW5zIHJlcHJlc2VudGluZyByaWNobmVzcyBhbmQNCmVkZ2VzIHJlcHJlc2VudGluZyBtb3ZlcyBhbG9uZyB0aGUgc3lzdGVtLg0KV2UgY2FuIGNvbG91ciB0aGUgZWRnZXMgdG8gcmVwcmVzZW50IHdoZXRoZXIgdGhleSB3ZXJlIHZpYSBhcnJpdmFsLCBkeW5hbWljDQpleHRpbmN0aW9uLCBvciBuZXV0cmFsIGV4dGluY3Rpb24gYXMgd2VsbC4NCg==